0. Background

1. Import gene set collections from MSigDB

h_df <- lapply(h_l, cbind)
Error in lapply(h_l, cbind) : object 'h_l' not found

1.1. Convert human entrezgene -> human ensembl

h_mapped <- readRDS(file.path(genesetsDir, "ens_h_mapped.rds"))
Error in file.path(genesetsDir, "ens_h_mapped.rds") : 
  object 'genesetsDir' not found

1.2. Convert human entrezgene -> mouse ensembl

# Mouse entrez and ensembl ids:
mEns <- org.Mm.egENSEMBL %>%
  as.data.frame %>%
  set_colnames(c("m_entrezgene","m_ensembl"))

# Create a data.frame to map between human entrezgenes and zebrafish ensembl IDs.
# BioMart only includes homolog mappings for ensembl IDs which is why we need to 
# retrieve human ensembl IDs, then join to the humanEntrezEns data.frame,
# in order to get the desired human entrezgenes to zebrafish ensembl ID mapping. 
mMart <- useMart("ENSEMBL_MART_ENSEMBL", "mmusculus_gene_ensembl")
getFromBiomart <- c("ensembl_gene_id", "hsapiens_homolog_ensembl_gene")
mAndHumanEnsGenes <- getBM(getFromBiomart, values = unique(mEns$m_ensembl), mart = mMart) %>%
  set_colnames(c("zebrafish_ensembl", "human_ensembl")) %>%
  left_join(human_entrez2Ens, by = "human_ensembl") %>%
  dplyr::select(-human_ensembl) %>% 
  dplyr::filter(complete.cases(.))

# mToHu <- getBM(c("ensembl_gene_id", "hsapiens_homolog_ensembl_gene"),
#                     values = unique(mEns$m_ensembl), mart = mMart) %>%
#   set_colnames(c("m_ensembl","hu_ensembl"))
mapHumanGS2Mouse<- function(x) {
  x %>% 
    as.data.frame %>% 
    set_colnames("human_entrezgene") %>%
    left_join(mAndHumanEnsGenes, by = "human_entrezgene") %>% 
    dplyr::filter(complete.cases(.)) %>%
    dplyr::select(-human_entrezgene) %>%
    as.list %>%
    unname %>%
    .[[1]] %>%
    unique
}

h_mapped_m <- lapply(h_df, mapHumanGS2Mouse)
c1_mapped_m <- lapply(c1_df, mapHumanGS2Mouse)
c2_mapped_m <- lapply(c2_df, mapHumanGS2Mouse) 
c3_mapped_m <- lapply(c3_df, mapHumanGS2Mouse)
c4_mapped_m <- lapply(c4_df, mapHumanGS2Mouse)
c5_mapped_m <- lapply(c5_df, mapHumanGS2Mouse)
c6_mapped_m <- lapply(c6_df, mapHumanGS2Mouse)
c7_mapped_m <- lapply(c7_df, mapHumanGS2Mouse)

h_mapped_m %>% saveRDS(file.path(genesetsDir, "ens_h_mapped_m.rds"))
c1_mapped_m %>% saveRDS(file.path(genesetsDir, "ens_c1_mapped_m.rds"))
c2_mapped_m %>% saveRDS(file.path(genesetsDir, "ens_c2_mapped_m.rds"))
c3_mapped_m %>% saveRDS(file.path(genesetsDir, "ens_c3_mapped_m.rds"))
c4_mapped_m %>% saveRDS(file.path(genesetsDir, "ens_c4_mapped_m.rds"))
c5_mapped_m %>% saveRDS(file.path(genesetsDir, "ens_c5_mapped_m.rds"))
c6_mapped_m %>% saveRDS(file.path(genesetsDir, "ens_c6_mapped_m.rds"))
c7_mapped_m %>% saveRDS(file.path(genesetsDir, "ens_c7_mapped_m.rds"))

h_mapped_m <- readRDS(file.path(genesetsDir, "ens_h_mapped_m.rds"))
c1_mapped_m <- readRDS(file.path(genesetsDir, "ens_c1_mapped_m.rds"))
c2_mapped_m <- readRDS(file.path(genesetsDir, "ens_c2_mapped_m.rds"))
c3_mapped_m <- readRDS(file.path(genesetsDir, "ens_c3_mapped_m.rds"))
c4_mapped_m <- readRDS(file.path(genesetsDir, "ens_c4_mapped_m.rds"))
c5_mapped_m <- readRDS(file.path(genesetsDir, "ens_c5_mapped_m.rds"))
c6_mapped_m <- readRDS(file.path(genesetsDir, "ens_c6_mapped_m.rds"))
c7_mapped_m <- readRDS(file.path(genesetsDir, "ens_c7_mapped_m.rds"))

2. Import IRE gene set

mouseIreGenes_h<-readRDS(here::here("R/IREGenes/data/mouseIreGenes_h.rds"))
humanIreGenes<-readRDS(here::here("R/IREGenes/data/human_ireGenes.rds"))
zebrafishIreGenes_h<-readRDS(here::here("R/IREGenes/data/zebrafishIreGenes_h.rds"))

3. Human: Overlap with GSEA Analysis

h_tib <- h_mapped %>% tibble %>% set_colnames(c("ids")) %>% mutate(geneset = names(h_mapped), source = "h") 
c2_tib <- c2_mapped %>% tibble %>% set_colnames(c("ids")) %>% mutate(geneset = names(c2_mapped), source = "c2")
c3_tib <- c3_mapped %>% tibble %>% set_colnames(c("ids")) %>% mutate(geneset = names(c3_mapped), source = "c3")
c5_tib <- c5_mapped %>% tibble %>% set_colnames(c("ids")) %>% mutate(geneset = names(c5_mapped), source = "c5")
gs <- bind_rows(h_tib, c2_tib, c3_tib, c5_tib)
gs
humanIreGenes%>%str
List of 4
 $ ire3_all: chr [1:3041] "ENSG00000187642" "ENSG00000162571" "ENSG00000224051" "ENSG00000107404" ...
 $ ire5_all: chr [1:1265] "ENSG00000188157" "ENSG00000162572" "ENSG00000175756" "ENSG00000235169" ...
 $ ire3_hq : chr [1:325] "ENSG00000162571" "ENSG00000248333" "ENSG00000215790" "ENSG00000169598" ...
 $ ire5_hq : chr [1:106] "ENSG00000175756" "ENSG00000176083" "ENSG00000085831" "ENSG00000177301" ...
allIREGenes <- c(humanIreGenes$ire3_all, humanIreGenes$ire5_all)
allIREGenes %>% head
[1] "ENSG00000187642" "ENSG00000162571" "ENSG00000224051" "ENSG00000107404" "ENSG00000242485" "ENSG00000179403"

3.1. Compute gene overlap with IRE genesets

We wish to see the overlap between the genesets in gs with the humanIreGenes (including 3’ and 5’ IRE genes), as well as separately, with the 3’ IRE genes and 5’ IRE genes. We will add this information into the gs tibble.

gs %<>% rowwise() %>% mutate(
  n = length(ids),  # Number of genes in the geneset
  
  n_with_ire = sum(ids %in% allIREGenes),  # Number of genes in the gene set which have 3' or 5' predicted IREs 
  n_without_ire = (n - n_with_ire),
  universe_with_ire = sum(human_entrez2Ens$human_ensembl %in% allIREGenes & !(human_entrez2Ens$human_ensembl %in% ids)), 
  universe_without_ire = (length(human_entrez2Ens$human_ensembl) - universe_with_ire),
  
  n_with_ire3 = sum(ids %in% humanIreGenes$ire3_all),  # Number of genes in the gene set which have 3' predicted IREs
  n_without_ire3= (n - n_with_ire),
  universe_with_ire3 = sum(human_entrez2Ens$human_ensembl %in% humanIreGenes$ire3_all & !(human_entrez2Ens$human_ensembl %in% ids)), 
  universe_without_ire3 = (length(human_entrez2Ens$human_ensembl) - universe_with_ire3),
  
  n_with_ire5 = sum(ids %in% humanIreGenes$ire5_all),  # Number of genes in the gene set which have 5' predicted IREs
  n_without_ire5= (n - n_with_ire),
  universe_with_ire5 = sum(human_entrez2Ens$human_ensembl %in% humanIreGenes$ire5_all & !(human_entrez2Ens$human_ensembl %in% ids)), 
  universe_without_ire5 = (length(human_entrez2Ens$human_ensembl) - universe_with_ire5)
) %>% ungroup()
gs
gs_mat <- gs %>% 
  dplyr::select(n_with_ire,
                n_without_ire, 
                universe_with_ire, 
                universe_without_ire) %>% 
  apply(X = ., MARGIN = 1, FUN = function(x){
    x %>% 
      matrix(2,2) %>% 
      t %>% 
      list()
  }) %>% set_names(gs$geneset) %>% 
  lapply(function(x){
    x %>% .[[1]]
  })

gs_mat3 <- gs %>% 
  dplyr::select(n_with_ire3,
                n_without_ire3, 
                universe_with_ire3, 
                universe_without_ire3) %>% 
  apply(X = ., MARGIN = 1, FUN = function(x){
    x %>% 
      matrix(2,2) %>% 
      t %>% 
      list()
  }) %>% set_names(gs$geneset) %>% 
  lapply(function(x){
    x %>% .[[1]]
  })

gs_mat5 <- gs %>% 
  dplyr::select(n_with_ire5,
                n_without_ire5, 
                universe_with_ire5, 
                universe_without_ire5) %>% 
  apply(X = ., MARGIN = 1, FUN = function(x){
    x %>% 
      matrix(2,2) %>% 
      t %>% 
      list()
  }) %>% set_names(gs$geneset) %>% 
  lapply(function(x){
    x %>% .[[1]]
  })
gs_mat[1:3]
$HALLMARK_TNFA_SIGNALING_VIA_NFKB
     [,1]  [,2]
[1,]   45   183
[2,] 3999 26904

$HALLMARK_HYPOXIA
     [,1]  [,2]
[1,]   51   163
[2,] 3993 26910

$HALLMARK_CHOLESTEROL_HOMEOSTASIS
     [,1]  [,2]
[1,]   18    60
[2,] 4026 26877
gs_mat3[1:3]
$HALLMARK_TNFA_SIGNALING_VIA_NFKB
     [,1]  [,2]
[1,]   31   183
[2,] 3004 26904

$HALLMARK_HYPOXIA
     [,1]  [,2]
[1,]   34   163
[2,] 3001 26910

$HALLMARK_CHOLESTEROL_HOMEOSTASIS
     [,1]  [,2]
[1,]   14    60
[2,] 3021 26877
gs_mat5[1:3]
$HALLMARK_TNFA_SIGNALING_VIA_NFKB
     [,1]  [,2]
[1,]   17   183
[2,] 1256 26904

$HALLMARK_HYPOXIA
     [,1]  [,2]
[1,]   19   163
[2,] 1254 26910

$HALLMARK_CHOLESTEROL_HOMEOSTASIS
     [,1]  [,2]
[1,]    5    60
[2,] 1268 26877
fisher_res <- gs_mat %>% lapply(function(x){
  x %>% fisher.test()
})
fisher_res3 <- gs_mat3 %>% lapply(function(x){
  x %>% fisher.test()
})
fisher_res5 <- gs_mat5 %>% lapply(function(x){
  x %>% fisher.test()
})
fisher_res_p <- fisher_res %>% lapply(function(x){x$p.value})
fisher_res_p3 <- fisher_res3 %>% lapply(function(x){x$p.value})
fisher_res_p5 <- fisher_res5 %>% lapply(function(x){x$p.value})
gs %<>% 
  mutate(fisher_p = fisher_res_p%>%unlist%>%unname,
         fisher_p_3 = fisher_res_p3%>%unlist%>%unname,
         fisher_p_5 = fisher_res_p5%>%unlist%>%unname) %>%
  mutate(fdr = p.adjust(fisher_p, "fdr"),
         fdr_3 = p.adjust(fisher_p_3, "fdr"),
         fdr_5 = p.adjust(fisher_p_5, "fdr"))

Calculate Expected and Observed

For each gene set, we will calculate the number of genes expected to have IREs (based on the background proportion) and observed value. This information will be added to the gs object.

gs %<>% rowwise() %>% mutate(
  exp_allIRE = (universe_with_ire / universe_without_ire)*n_without_ire,
  obs_allIRE = n_with_ire,
  obs_greater_than_exp_allIRE = obs_allIRE > exp_allIRE,
  
  exp_ire3 = (universe_with_ire3 / universe_without_ire3)*n_without_ire3,
  obs_ire3 = n_with_ire3,
  obs_greater_than_exp_ire3 = obs_ire3 > exp_ire3,
  
  exp_ire5 = (universe_with_ire5 / universe_without_ire5)*n_without_ire5,
  obs_ire5 = n_with_ire5,
  obs_greater_than_exp_ire5 = obs_ire5 > exp_ire5
) 
gs %>%
  dplyr::select(geneset, contains("exp"), starts_with("obs"), ) %>% ungroup()

6. Results

The following table shows the top 50 gene sets enriched in IRE genesets, ranked by Fisher’s exact test p-values:

gs %>% ungroup() %>%
  arrange(fisher_p) %>%
  dplyr::select(geneset, contains("fdr"), n_with_ire, n, starts_with("obs_greater")) 
gs %>% saveRDS(here::here("R/GSEA/data/ora/human_gs.rds"))

Sorted by genesets most enriched in 3’ IRE genes:

gs %>% ungroup %>% arrange(fisher_p_3) %>% dplyr::select(geneset, contains("fdr"), n_with_ire3, n, starts_with("obs_greater")) %>% head(50)

Sorted by genesets most enriched in 5’ IRE genes:

gs %>% ungroup %>% arrange(fisher_p_5) %>% dplyr::select(geneset, contains("fdr"), n_with_ire5, n, starts_with("obs_greater")) %>% head(50)

7. Visualisation (Stacked bar chart)

The following stacked bar chart shows the overlap between the top ~20 genesets and the predicted-IRE genesets.

8. Visualisation (network overlap)

gsComb$common_ids%>%
  unlist %>% 
  table %>% 
  as.data.frame%>%
  arrange(desc(Freq)) %>%
  set_colnames(c("ensembl_gene_id", "freq"))  %>% head
  ensembl_gene_id freq
1 ENSG00000185043   91
2 ENSG00000010810   78
3 ENSG00000082701   78
4 ENSG00000100030   78
5 ENSG00000102081   66
6 ENSG00000148498   66
head(nodesDf2, 30) 
                                        Id    ire
1            BLALOCK_ALZHEIMERS_DISEASE_UP       
2                           AACTTT_UNKNOWN       
3                    PILON_KLF1_TARGETS_DN       
4                  GO_CELL_PROJECTION_PART       
5          GO_CELL_PROJECTION_ORGANIZATION       
6   GRAESSMANN_APOPTOSIS_BY_DOXORUBICIN_DN       
7                           GO_NEURON_PART       
8                          GO_NEUROGENESIS       
9               GO_INTRACELLULAR_TRANSPORT       
10                      PEREZ_TP53_TARGETS       
11 GO_REGULATION_OF_ORGANELLE_ORGANIZATION       
12     GO_CELLULAR_COMPONENT_MORPHOGENESIS       
13  GO_CELLULAR_MACROMOLECULE_LOCALIZATION       
14      GO_RESPONSE_TO_ENDOGENOUS_STIMULUS       
15                    GO_NEURON_PROJECTION       
16                  Predicted 3' IRE genes      3
17                  Predicted 5' IRE genes      5
18                HALLMARK_HEME_METABOLISM       
19                         ENSG00000205307      5
20                         ENSG00000164692 no IRE
21                         ENSG00000112561      3
22                         ENSG00000177302 no IRE
23                         ENSG00000284238 no IRE
24                         ENSG00000076716 no IRE
25                         ENSG00000137364 no IRE
26                         ENSG00000164292      3
27                         ENSG00000113658 no IRE
28                         ENSG00000176170      5
29                         ENSG00000156103 no IRE
30                         ENSG00000104936 no IRE
edgeDf %>% head(20)
                          Source          Target
1  BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000205307
2  BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000164692
3  BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000112561
4  BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000177302
5  BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000284238
6  BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000076716
7  BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000137364
8  BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000164292
9  BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000113658
10 BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000176170
11 BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000156103
12 BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000104936
13 BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000197429
14 BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000140836
15 BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000132330
16 BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000145819
17 BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000230989
18 BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000211455
19 BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000006652
20 BLALOCK_ALZHEIMERS_DISEASE_UP ENSG00000142541

9. Session Info

sessionInfo()
R version 3.5.2 (2018-12-20)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS Mojave 10.14.6

Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/3.5/Resources/lib/libRlapack.dylib

locale:
[1] en_AU.UTF-8/en_AU.UTF-8/en_AU.UTF-8/C/en_AU.UTF-8/en_AU.UTF-8

attached base packages:
[1] stats4    parallel  stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] pheatmap_1.0.12        biomaRt_2.38.0         org.Hs.eg.db_3.7.0     GSEABase_1.44.0        graph_1.60.0           annotate_1.60.1       
 [7] XML_3.98-1.20          gdtools_0.2.1          UpSetR_1.4.0           org.Dr.eg.db_3.7.0     fgsea_1.8.0            Rcpp_1.0.3            
[13] reshape2_1.4.3         export_0.2.2           purrr_0.3.3            stringr_1.4.0          ggplot2_3.2.1          openxlsx_4.1.3        
[19] here_0.1               readr_1.3.1            dplyr_0.8.3            tibble_2.1.3           magrittr_1.5           org.Mm.eg.db_3.7.0    
[25] ensembldb_2.6.8        AnnotationFilter_1.6.0 GenomicFeatures_1.34.8 AnnotationDbi_1.44.0   GenomicRanges_1.34.0   GenomeInfoDb_1.18.2   
[31] IRanges_2.16.0         S4Vectors_0.20.1       edgeR_3.24.3           limma_3.38.3           AnnotationHub_2.14.5   affy_1.60.0           
[37] Biobase_2.42.0         BiocGenerics_0.28.0   

loaded via a namespace (and not attached):
 [1] colorspace_1.4-1              rprojroot_1.3-2               flextable_0.5.6               XVector_0.22.0               
 [5] base64enc_0.1-3               rstudioapi_0.10               affyio_1.52.0                 bit64_0.9-7                  
 [9] interactiveDisplayBase_1.20.0 fansi_0.4.0                   xml2_1.2.2                    knitr_1.26                   
[13] zeallot_0.1.0                 jsonlite_1.6                  Rsamtools_1.34.1              broom_0.5.2                  
[17] shiny_1.4.0                   BiocManager_1.30.9            compiler_3.5.2                httr_1.4.1                   
[21] backports_1.1.5               assertthat_0.2.1              Matrix_1.2-17                 fastmap_1.0.1                
[25] lazyeval_0.2.2                cli_1.1.0                     later_1.0.0                   htmltools_0.4.0              
[29] prettyunits_1.0.2             tools_3.5.2                   gtable_0.3.0                  glue_1.3.1                   
[33] GenomeInfoDbData_1.2.0        fastmatch_1.1-0               vctrs_0.2.0                   Biostrings_2.50.2            
[37] preprocessCore_1.44.0         nlme_3.1-142                  stargazer_5.2.2               rtracklayer_1.42.2           
[41] crosstalk_1.0.0               xfun_0.11                     miniUI_0.1.1.1                mime_0.7                     
[45] lifecycle_0.1.0               zlibbioc_1.28.0               scales_1.0.0                  hms_0.5.2                    
[49] promises_1.1.0                ProtGenerics_1.14.0           SummarizedExperiment_1.12.0   RColorBrewer_1.1-2           
[53] yaml_2.2.0                    curl_4.2                      gridExtra_2.3                 memoise_1.1.0                
[57] stringi_1.4.3                 RSQLite_2.1.2                 manipulateWidget_0.10.0       zip_2.0.4                    
[61] BiocParallel_1.16.6           rlang_0.4.1                   pkgconfig_2.0.3               systemfonts_0.1.1            
[65] matrixStats_0.55.0            bitops_1.0-6                  rgl_0.100.30                  evaluate_0.14                
[69] lattice_0.20-38               labeling_0.3                  htmlwidgets_1.5.1             GenomicAlignments_1.18.1     
[73] rvg_0.2.2                     bit_1.1-14                    tidyselect_0.2.5              plyr_1.8.4                   
[77] R6_2.4.1                      generics_0.0.2                DelayedArray_0.8.0            DBI_1.0.0                    
[81] pillar_1.4.2                  withr_2.1.2                   RCurl_1.95-4.12               crayon_1.3.4                 
[85] uuid_0.1-2                    utf8_1.1.4                    rmarkdown_1.17                officer_0.3.6                
[89] progress_1.2.2                locfit_1.5-9.1                grid_3.5.2                    data.table_1.12.6            
[93] blob_1.2.0                    webshot_0.5.1                 digest_0.6.22                 xtable_1.8-4                 
[97] tidyr_1.0.0                   httpuv_1.5.2                  munsell_0.5.0                
LS0tCnRpdGxlOiAiR1NFQSBvdmVybGFwIHdpdGggSVJFIGdlbmVzIC0gTW91c2UgIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KEdTRUFCYXNlKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KG1hZ3JpdHRyKQpsaWJyYXJ5KHRpYmJsZSkKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShmZ3NlYSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKbGlicmFyeShvcmcuRHIuZWcuZGIpCmxpYnJhcnkob3JnLk1tLmVnLmRiKQpsaWJyYXJ5KGJpb21hUnQpCmxpYnJhcnkobGltbWEpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShvcGVueGxzeCkKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShoZXJlKQpgYGAKCgojIyAwLiBCYWNrZ3JvdW5kCgotIEluIHRoZSBgR1NFQV9PdmVybGFwX3dpdGhfSVJFR2VuZXMuUm1kYCBub3RlYm9vaywgRmlzaGVyJ3MgZXhhY3QgdGVzdCB3YXMgdXNlZCB0byBkZXRlcm1pbmUgd2hldGhlciBNU2lnREIgZ2VuZSBzZXRzIHdlcmUgZW5yaWNoZWQgaW4gdGhlIHplYnJhZmlzaCBwcmVkaWN0ZWQgSVJFIGdlbmUgc2V0cyB3ZSBkZWZpbmVkLiAKLSBOb3cgd2UgYXJlIGdvaW5nIHRvIHJlcGVhdCB0aGUgc2FtZSBhbmFseXNpcywgYnV0IGZvciB0aGUgbW91c2UgcHJlZGljdGVkIElSRSBnZW5lIHNldHMuCgojIyAxLiBJbXBvcnQgZ2VuZSBzZXQgY29sbGVjdGlvbnMgZnJvbSBNU2lnREIKCi0gQXMgaW4gdGhlIG9yaWdpbmFsIGFuYWx5c2lzLCB0aGUgZm9sbG93aW5nIGNvbGxlY3Rpb25zIGZyb20gTVNpZ0RCIHdpbGwgYmUgaW1wb3J0ZWQ6CiAgLSBIYWxsbWFyayBnZW5lIHNldHMKICAtIEMyIC0gQ3VyYXRlZCBnZW5lIHNldHMKICAtIEMzIC0gTW90aWYgZ2VuZSBzZXRzCiAgLSBDNCAtIEdlbmUgb250b2xvZ3kgZ2VuZSBzZXRzCi0gQXMgaHVtYW4gZG93bmxvYWRzIGFyZSByZWFkaWx5IGF2YWlsYWJsZSBmcm9tIHRoZSBNU2lnREIsIHdlIHdpbGwgbm90IG5lZWQgdG8gY29udmVydCBiZXR3ZWVuIHNwZWNpZXMuIAotIE1vdXNlIGlzIG5vdCBhdmFpbGFibGUgZnJvbSBNU2lnREIuIFtXRUhJXShodHRwOi8vYmlvaW5mLndlaGkuZWR1LmF1L3NvZnR3YXJlL01TaWdEQi8pIGhhdmUgYSBkb3dubG9hZCBidXQgaXQncyBmb3IgdmVyc2lvbiA1LjIgc28gdG8gbWFpbnRhaW4gY29uc2lzdGVuY3kgd2l0aCB0aGUgaHVtYW4gb25lLCB3ZSB3aWxsIGRvIGEgZnJlc2ggY29udmVyc2lvbi4gCi0gSG93ZXZlciwgd2Ugd2lsbCBuZWVkIHRvIGNvbnZlcnQgdGhlIElEIGZyb20gZW50cmV6Z2VuZSBpbnRvIEVuc2VtYmwgSUQgdG8gcmVtYWluIGNvbnNpc3RlbnQgYWNyb3NzIGFsbCBvZiBvdXIgYW5hbHlzZXMuIAoKYGBge3J9CmdlbmVzZXRzRGlyIDwtIGhlcmU6OmhlcmUoIlIiLCAiR1NFQSIsICJkYXRhIiwgImh1bWFuIikKCmggPC0gZ2V0R210KGZpbGUucGF0aChnZW5lc2V0c0RpciwgImguYWxsLnY3LjAuZW50cmV6LmdtdCIpKSAgICAjIEhhbGxtYXJrIGdlbmUgc2V0cwpjMSA8LSBnZXRHbXQoZmlsZS5wYXRoKGdlbmVzZXRzRGlyLCAiYzEuYWxsLnY3LjAuZW50cmV6LmdtdCIpKSAgIyBwb3NpdGlvbmFsIGdlbmUgc2V0cwpjMiA8LSBnZXRHbXQoZmlsZS5wYXRoKGdlbmVzZXRzRGlyLCAiYzIuYWxsLnY3LjAuZW50cmV6LmdtdCIpKSAgIyBjdXJhdGVkIGdlbmUgc2V0cwpjMyA8LSBnZXRHbXQoZmlsZS5wYXRoKGdlbmVzZXRzRGlyLCAiYzMuYWxsLnY3LjAuZW50cmV6LmdtdCIpKSAgIyBtb3RpZiBnZW5lIHNldHMKYzQgPC0gZ2V0R210KGZpbGUucGF0aChnZW5lc2V0c0RpciwgImM0LmFsbC52Ny4wLmVudHJlei5nbXQiKSkgICMgY29tcHV0YXRpb25hbCBnZW5lIHNldHMKYzUgPC0gZ2V0R210KGZpbGUucGF0aChnZW5lc2V0c0RpciwgImM1LmFsbC52Ny4wLmVudHJlei5nbXQiKSkgICMgR08gZ2VuZSBzZXRzCmM2IDwtIGdldEdtdChmaWxlLnBhdGgoZ2VuZXNldHNEaXIsICJjNi5hbGwudjcuMC5lbnRyZXouZ210IikpICAjIG9uY29nZW5pYyBzaWduYXR1cmVzIGdlbmUgc2V0cwpjNyA8LSBnZXRHbXQoZmlsZS5wYXRoKGdlbmVzZXRzRGlyLCAiYzcuYWxsLnY3LjAuZW50cmV6LmdtdCIpKSAgIyBpbW11bm9sb2dpYyBzaWduYXR1cmVzIGdlbmUgc2V0cwoKCiMgQ29udmVydCBlYWNoIGdlbmUgc2V0cyB0byBsaXN0IHdoZXJlIHRoZSBuYW1lIG9mIGVhY2ggbGlzdCBpcyB0aGUgZ2VuZSBzZXQKIyBuYW1lIGFuZCB0aGUgbGlzdCBpdGVtcyBhcmUgdGhlIGVudHJlemdlbmVzLiAKaF9sIDwtIGdlbmVJZHMoaCkgJT4lIGFzLmxpc3QKYzFfbCA8LSBnZW5lSWRzKGMxKSAlPiUgYXMubGlzdApjMl9sIDwtIGdlbmVJZHMoYzIpICU+JSBhcy5saXN0CmMzX2wgPC0gZ2VuZUlkcyhjMykgJT4lIGFzLmxpc3QKYzRfbCA8LSBnZW5lSWRzKGM0KSAlPiUgYXMubGlzdApjNV9sIDwtIGdlbmVJZHMoYzUpICU+JSBhcy5saXN0CmM2X2wgPC0gZ2VuZUlkcyhjNikgJT4lIGFzLmxpc3QKYzdfbCA8LSBnZW5lSWRzKGM3KSAlPiUgYXMubGlzdAoKIyBCaW5kIHRoZSBsaXN0IG9mIGdlbmUgc2V0cyBzbyB0aGF0IGVhY2ggbGlzdCBiZWNvbWVzIGEgZGF0YS5mcmFtZS4KaF9kZiA8LSBsYXBwbHkoaF9sLCBjYmluZCkKYzFfZGYgPC0gbGFwcGx5KGMxX2wsIGNiaW5kKQpjMl9kZiA8LSBsYXBwbHkoYzJfbCwgY2JpbmQpCmMzX2RmIDwtIGxhcHBseShjM19sLCBjYmluZCkKYzRfZGYgPC0gbGFwcGx5KGM0X2wsIGNiaW5kKQpjNV9kZiA8LSBsYXBwbHkoYzVfbCwgY2JpbmQpCmM2X2RmIDwtIGxhcHBseShjNl9sLCBjYmluZCkKYzdfZGYgPC0gbGFwcGx5KGM3X2wsIGNiaW5kKQpgYGAKCiMjIDEuMS4gQ29udmVydCBodW1hbiBlbnRyZXpnZW5lIC0+IGh1bWFuIGVuc2VtYmwKCmBgYHtyfQpodW1hbl9lbnRyZXoyRW5zIDwtIGFzLmRhdGEuZnJhbWUob3JnLkhzLmVnRU5TRU1CTCkgJT4lCiAgc2V0X2NvbG5hbWVzKGMoImh1bWFuX2VudHJlemdlbmUiLCAiaHVtYW5fZW5zZW1ibCIpKQoKbWFwX2h1bWFuX2VudHJlejJFbnMgPC0gZnVuY3Rpb24oeCkgewogIHggJT4lIAogICAgYXMuZGF0YS5mcmFtZSAlPiUgCiAgICBzZXRfY29sbmFtZXMoImh1bWFuX2VudHJlemdlbmUiKSAlPiUKICAgIGxlZnRfam9pbihodW1hbl9lbnRyZXoyRW5zLCBieSA9ICJodW1hbl9lbnRyZXpnZW5lIikgJT4lIAogICAgZHBseXI6OmZpbHRlcihjb21wbGV0ZS5jYXNlcyguKSkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KC1odW1hbl9lbnRyZXpnZW5lKSAlPiUKICAgIGFzLmxpc3QgJT4lCiAgICB1bm5hbWUgJT4lCiAgICAuW1sxXV0gJT4lCiAgICB1bmlxdWUKfQoKaF9tYXBwZWQgPC0gbGFwcGx5KGhfZGYsIG1hcF9odW1hbl9lbnRyZXoyRW5zKQpjMV9tYXBwZWQgPC0gbGFwcGx5KGMxX2RmLCBtYXBfaHVtYW5fZW50cmV6MkVucykKYzJfbWFwcGVkIDwtIGxhcHBseShjMl9kZiwgbWFwX2h1bWFuX2VudHJlejJFbnMpIApjM19tYXBwZWQgPC0gbGFwcGx5KGMzX2RmLCBtYXBfaHVtYW5fZW50cmV6MkVucykKYzRfbWFwcGVkIDwtIGxhcHBseShjNF9kZiwgbWFwX2h1bWFuX2VudHJlejJFbnMpCmM1X21hcHBlZCA8LSBsYXBwbHkoYzVfZGYsIG1hcF9odW1hbl9lbnRyZXoyRW5zKQpjNl9tYXBwZWQgPC0gbGFwcGx5KGM2X2RmLCBtYXBfaHVtYW5fZW50cmV6MkVucykKYzdfbWFwcGVkIDwtIGxhcHBseShjN19kZiwgbWFwX2h1bWFuX2VudHJlejJFbnMpCgpoX21hcHBlZCAlPiUgc2F2ZVJEUyhmaWxlLnBhdGgoZ2VuZXNldHNEaXIsICJlbnNfaF9tYXBwZWQucmRzIikpCmMxX21hcHBlZCAlPiUgc2F2ZVJEUyhmaWxlLnBhdGgoZ2VuZXNldHNEaXIsICJlbnNfYzFfbWFwcGVkLnJkcyIpKQpjMl9tYXBwZWQgJT4lIHNhdmVSRFMoZmlsZS5wYXRoKGdlbmVzZXRzRGlyLCAiZW5zX2MyX21hcHBlZC5yZHMiKSkKYzNfbWFwcGVkICU+JSBzYXZlUkRTKGZpbGUucGF0aChnZW5lc2V0c0RpciwgImVuc19jM19tYXBwZWQucmRzIikpCmM0X21hcHBlZCAlPiUgc2F2ZVJEUyhmaWxlLnBhdGgoZ2VuZXNldHNEaXIsICJlbnNfYzRfbWFwcGVkLnJkcyIpKQpjNV9tYXBwZWQgJT4lIHNhdmVSRFMoZmlsZS5wYXRoKGdlbmVzZXRzRGlyLCAiZW5zX2M1X21hcHBlZC5yZHMiKSkKYzZfbWFwcGVkICU+JSBzYXZlUkRTKGZpbGUucGF0aChnZW5lc2V0c0RpciwgImVuc19jNl9tYXBwZWQucmRzIikpCmM3X21hcHBlZCAlPiUgc2F2ZVJEUyhmaWxlLnBhdGgoZ2VuZXNldHNEaXIsICJlbnNfYzdfbWFwcGVkLnJkcyIpKQoKaF9tYXBwZWQgPC0gcmVhZFJEUyhmaWxlLnBhdGgoZ2VuZXNldHNEaXIsICJlbnNfaF9tYXBwZWQucmRzIikpCmMxX21hcHBlZCA8LSByZWFkUkRTKGZpbGUucGF0aChnZW5lc2V0c0RpciwgImVuc19jMV9tYXBwZWQucmRzIikpCmMyX21hcHBlZCA8LSByZWFkUkRTKGZpbGUucGF0aChnZW5lc2V0c0RpciwgImVuc19jMl9tYXBwZWQucmRzIikpCmMzX21hcHBlZCA8LSByZWFkUkRTKGZpbGUucGF0aChnZW5lc2V0c0RpciwgImVuc19jM19tYXBwZWQucmRzIikpCmM0X21hcHBlZCA8LSByZWFkUkRTKGZpbGUucGF0aChnZW5lc2V0c0RpciwgImVuc19jNF9tYXBwZWQucmRzIikpCmM1X21hcHBlZCA8LSByZWFkUkRTKGZpbGUucGF0aChnZW5lc2V0c0RpciwgImVuc19jNV9tYXBwZWQucmRzIikpCmM2X21hcHBlZCA8LSByZWFkUkRTKGZpbGUucGF0aChnZW5lc2V0c0RpciwgImVuc19jNl9tYXBwZWQucmRzIikpCmM3X21hcHBlZCA8LSByZWFkUkRTKGZpbGUucGF0aChnZW5lc2V0c0RpciwgImVuc19jN19tYXBwZWQucmRzIikpCgpgYGAKCiMjIDEuMi4gQ29udmVydCBodW1hbiBlbnRyZXpnZW5lIC0+IG1vdXNlIGVuc2VtYmwKCmBgYHtyfQojIE1vdXNlIGVudHJleiBhbmQgZW5zZW1ibCBpZHM6Cm1FbnMgPC0gb3JnLk1tLmVnRU5TRU1CTCAlPiUKICBhcy5kYXRhLmZyYW1lICU+JQogIHNldF9jb2xuYW1lcyhjKCJtX2VudHJlemdlbmUiLCJtX2Vuc2VtYmwiKSkKCiMgQ3JlYXRlIGEgZGF0YS5mcmFtZSB0byBtYXAgYmV0d2VlbiBodW1hbiBlbnRyZXpnZW5lcyBhbmQgemVicmFmaXNoIGVuc2VtYmwgSURzLgojIEJpb01hcnQgb25seSBpbmNsdWRlcyBob21vbG9nIG1hcHBpbmdzIGZvciBlbnNlbWJsIElEcyB3aGljaCBpcyB3aHkgd2UgbmVlZCB0byAKIyByZXRyaWV2ZSBodW1hbiBlbnNlbWJsIElEcywgdGhlbiBqb2luIHRvIHRoZSBodW1hbkVudHJlekVucyBkYXRhLmZyYW1lLAojIGluIG9yZGVyIHRvIGdldCB0aGUgZGVzaXJlZCBodW1hbiBlbnRyZXpnZW5lcyB0byB6ZWJyYWZpc2ggZW5zZW1ibCBJRCBtYXBwaW5nLiAKbU1hcnQgPC0gdXNlTWFydCgiRU5TRU1CTF9NQVJUX0VOU0VNQkwiLCAibW11c2N1bHVzX2dlbmVfZW5zZW1ibCIpCmdldEZyb21CaW9tYXJ0IDwtIGMoImVuc2VtYmxfZ2VuZV9pZCIsICJoc2FwaWVuc19ob21vbG9nX2Vuc2VtYmxfZ2VuZSIpCm1BbmRIdW1hbkVuc0dlbmVzIDwtIGdldEJNKGdldEZyb21CaW9tYXJ0LCB2YWx1ZXMgPSB1bmlxdWUobUVucyRtX2Vuc2VtYmwpLCBtYXJ0ID0gbU1hcnQpICU+JQogIHNldF9jb2xuYW1lcyhjKCJ6ZWJyYWZpc2hfZW5zZW1ibCIsICJodW1hbl9lbnNlbWJsIikpICU+JQogIGxlZnRfam9pbihodW1hbl9lbnRyZXoyRW5zLCBieSA9ICJodW1hbl9lbnNlbWJsIikgJT4lCiAgZHBseXI6OnNlbGVjdCgtaHVtYW5fZW5zZW1ibCkgJT4lIAogIGRwbHlyOjpmaWx0ZXIoY29tcGxldGUuY2FzZXMoLikpCgojIG1Ub0h1IDwtIGdldEJNKGMoImVuc2VtYmxfZ2VuZV9pZCIsICJoc2FwaWVuc19ob21vbG9nX2Vuc2VtYmxfZ2VuZSIpLAojICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gdW5pcXVlKG1FbnMkbV9lbnNlbWJsKSwgbWFydCA9IG1NYXJ0KSAlPiUKIyAgIHNldF9jb2xuYW1lcyhjKCJtX2Vuc2VtYmwiLCJodV9lbnNlbWJsIikpCgpgYGAKCmBgYHtyfQptYXBIdW1hbkdTMk1vdXNlPC0gZnVuY3Rpb24oeCkgewogIHggJT4lIAogICAgYXMuZGF0YS5mcmFtZSAlPiUgCiAgICBzZXRfY29sbmFtZXMoImh1bWFuX2VudHJlemdlbmUiKSAlPiUKICAgIGxlZnRfam9pbihtQW5kSHVtYW5FbnNHZW5lcywgYnkgPSAiaHVtYW5fZW50cmV6Z2VuZSIpICU+JSAKICAgIGRwbHlyOjpmaWx0ZXIoY29tcGxldGUuY2FzZXMoLikpICU+JQogICAgZHBseXI6OnNlbGVjdCgtaHVtYW5fZW50cmV6Z2VuZSkgJT4lCiAgICBhcy5saXN0ICU+JQogICAgdW5uYW1lICU+JQogICAgLltbMV1dICU+JQogICAgdW5pcXVlCn0KCmhfbWFwcGVkX20gPC0gbGFwcGx5KGhfZGYsIG1hcEh1bWFuR1MyTW91c2UpCmMxX21hcHBlZF9tIDwtIGxhcHBseShjMV9kZiwgbWFwSHVtYW5HUzJNb3VzZSkKYzJfbWFwcGVkX20gPC0gbGFwcGx5KGMyX2RmLCBtYXBIdW1hbkdTMk1vdXNlKSAKYzNfbWFwcGVkX20gPC0gbGFwcGx5KGMzX2RmLCBtYXBIdW1hbkdTMk1vdXNlKQpjNF9tYXBwZWRfbSA8LSBsYXBwbHkoYzRfZGYsIG1hcEh1bWFuR1MyTW91c2UpCmM1X21hcHBlZF9tIDwtIGxhcHBseShjNV9kZiwgbWFwSHVtYW5HUzJNb3VzZSkKYzZfbWFwcGVkX20gPC0gbGFwcGx5KGM2X2RmLCBtYXBIdW1hbkdTMk1vdXNlKQpjN19tYXBwZWRfbSA8LSBsYXBwbHkoYzdfZGYsIG1hcEh1bWFuR1MyTW91c2UpCgpoX21hcHBlZF9tICU+JSBzYXZlUkRTKGZpbGUucGF0aChnZW5lc2V0c0RpciwgImVuc19oX21hcHBlZF9tLnJkcyIpKQpjMV9tYXBwZWRfbSAlPiUgc2F2ZVJEUyhmaWxlLnBhdGgoZ2VuZXNldHNEaXIsICJlbnNfYzFfbWFwcGVkX20ucmRzIikpCmMyX21hcHBlZF9tICU+JSBzYXZlUkRTKGZpbGUucGF0aChnZW5lc2V0c0RpciwgImVuc19jMl9tYXBwZWRfbS5yZHMiKSkKYzNfbWFwcGVkX20gJT4lIHNhdmVSRFMoZmlsZS5wYXRoKGdlbmVzZXRzRGlyLCAiZW5zX2MzX21hcHBlZF9tLnJkcyIpKQpjNF9tYXBwZWRfbSAlPiUgc2F2ZVJEUyhmaWxlLnBhdGgoZ2VuZXNldHNEaXIsICJlbnNfYzRfbWFwcGVkX20ucmRzIikpCmM1X21hcHBlZF9tICU+JSBzYXZlUkRTKGZpbGUucGF0aChnZW5lc2V0c0RpciwgImVuc19jNV9tYXBwZWRfbS5yZHMiKSkKYzZfbWFwcGVkX20gJT4lIHNhdmVSRFMoZmlsZS5wYXRoKGdlbmVzZXRzRGlyLCAiZW5zX2M2X21hcHBlZF9tLnJkcyIpKQpjN19tYXBwZWRfbSAlPiUgc2F2ZVJEUyhmaWxlLnBhdGgoZ2VuZXNldHNEaXIsICJlbnNfYzdfbWFwcGVkX20ucmRzIikpCgpoX21hcHBlZF9tIDwtIHJlYWRSRFMoZmlsZS5wYXRoKGdlbmVzZXRzRGlyLCAiZW5zX2hfbWFwcGVkX20ucmRzIikpCmMxX21hcHBlZF9tIDwtIHJlYWRSRFMoZmlsZS5wYXRoKGdlbmVzZXRzRGlyLCAiZW5zX2MxX21hcHBlZF9tLnJkcyIpKQpjMl9tYXBwZWRfbSA8LSByZWFkUkRTKGZpbGUucGF0aChnZW5lc2V0c0RpciwgImVuc19jMl9tYXBwZWRfbS5yZHMiKSkKYzNfbWFwcGVkX20gPC0gcmVhZFJEUyhmaWxlLnBhdGgoZ2VuZXNldHNEaXIsICJlbnNfYzNfbWFwcGVkX20ucmRzIikpCmM0X21hcHBlZF9tIDwtIHJlYWRSRFMoZmlsZS5wYXRoKGdlbmVzZXRzRGlyLCAiZW5zX2M0X21hcHBlZF9tLnJkcyIpKQpjNV9tYXBwZWRfbSA8LSByZWFkUkRTKGZpbGUucGF0aChnZW5lc2V0c0RpciwgImVuc19jNV9tYXBwZWRfbS5yZHMiKSkKYzZfbWFwcGVkX20gPC0gcmVhZFJEUyhmaWxlLnBhdGgoZ2VuZXNldHNEaXIsICJlbnNfYzZfbWFwcGVkX20ucmRzIikpCmM3X21hcHBlZF9tIDwtIHJlYWRSRFMoZmlsZS5wYXRoKGdlbmVzZXRzRGlyLCAiZW5zX2M3X21hcHBlZF9tLnJkcyIpKQpgYGAKCgoKIyMgMi4gSW1wb3J0IElSRSBnZW5lIHNldAoKYGBge3J9Cm1vdXNlSXJlR2VuZXNfaDwtcmVhZFJEUyhoZXJlOjpoZXJlKCJSL0lSRUdlbmVzL2RhdGEvbW91c2VJcmVHZW5lc19oLnJkcyIpKQpodW1hbklyZUdlbmVzPC1yZWFkUkRTKGhlcmU6OmhlcmUoIlIvSVJFR2VuZXMvZGF0YS9odW1hbl9pcmVHZW5lcy5yZHMiKSkKemVicmFmaXNoSXJlR2VuZXNfaDwtcmVhZFJEUyhoZXJlOjpoZXJlKCJSL0lSRUdlbmVzL2RhdGEvemVicmFmaXNoSXJlR2VuZXNfaC5yZHMiKSkKYGBgCgojIyAzLiBIdW1hbjogT3ZlcmxhcCB3aXRoIEdTRUEgQW5hbHlzaXMgCgpgYGB7cn0KaF90aWIgPC0gaF9tYXBwZWQgJT4lIHRpYmJsZSAlPiUgc2V0X2NvbG5hbWVzKGMoImlkcyIpKSAlPiUgbXV0YXRlKGdlbmVzZXQgPSBuYW1lcyhoX21hcHBlZCksIHNvdXJjZSA9ICJoIikgCmMyX3RpYiA8LSBjMl9tYXBwZWQgJT4lIHRpYmJsZSAlPiUgc2V0X2NvbG5hbWVzKGMoImlkcyIpKSAlPiUgbXV0YXRlKGdlbmVzZXQgPSBuYW1lcyhjMl9tYXBwZWQpLCBzb3VyY2UgPSAiYzIiKQpjM190aWIgPC0gYzNfbWFwcGVkICU+JSB0aWJibGUgJT4lIHNldF9jb2xuYW1lcyhjKCJpZHMiKSkgJT4lIG11dGF0ZShnZW5lc2V0ID0gbmFtZXMoYzNfbWFwcGVkKSwgc291cmNlID0gImMzIikKYzVfdGliIDwtIGM1X21hcHBlZCAlPiUgdGliYmxlICU+JSBzZXRfY29sbmFtZXMoYygiaWRzIikpICU+JSBtdXRhdGUoZ2VuZXNldCA9IG5hbWVzKGM1X21hcHBlZCksIHNvdXJjZSA9ICJjNSIpCgpncyA8LSBiaW5kX3Jvd3MoaF90aWIsIGMyX3RpYiwgYzNfdGliLCBjNV90aWIpCgpncwpgYGAKCmBgYHtyfQpodW1hbklyZUdlbmVzJT4lc3RyCgphbGxJUkVHZW5lcyA8LSBjKGh1bWFuSXJlR2VuZXMkaXJlM19hbGwsIGh1bWFuSXJlR2VuZXMkaXJlNV9hbGwpCgphbGxJUkVHZW5lcyAlPiUgaGVhZApgYGAKCiMjIyAzLjEuIENvbXB1dGUgZ2VuZSBvdmVybGFwIHdpdGggSVJFIGdlbmVzZXRzCgpXZSB3aXNoIHRvIHNlZSB0aGUgb3ZlcmxhcCBiZXR3ZWVuIHRoZSBnZW5lc2V0cyBpbiBgZ3NgIHdpdGggdGhlIGBodW1hbklyZUdlbmVzYCAoaW5jbHVkaW5nIDMnIGFuZCA1JyBJUkUgZ2VuZXMpLCAKYXMgd2VsbCBhcyBzZXBhcmF0ZWx5LCB3aXRoIHRoZSAzJyBJUkUgZ2VuZXMgYW5kIDUnIElSRSBnZW5lcy4gV2Ugd2lsbCBhZGQgdGhpcyBpbmZvcm1hdGlvbiBpbnRvIHRoZSBgZ3NgIHRpYmJsZS4KCgpgYGB7cn0KZ3MgJTw+JSByb3d3aXNlKCkgJT4lIG11dGF0ZSgKICBuID0gbGVuZ3RoKGlkcyksICAjIE51bWJlciBvZiBnZW5lcyBpbiB0aGUgZ2VuZXNldAogIAogIG5fd2l0aF9pcmUgPSBzdW0oaWRzICVpbiUgYWxsSVJFR2VuZXMpLCAgIyBOdW1iZXIgb2YgZ2VuZXMgaW4gdGhlIGdlbmUgc2V0IHdoaWNoIGhhdmUgMycgb3IgNScgcHJlZGljdGVkIElSRXMgCiAgbl93aXRob3V0X2lyZSA9IChuIC0gbl93aXRoX2lyZSksCiAgdW5pdmVyc2Vfd2l0aF9pcmUgPSBzdW0oaHVtYW5fZW50cmV6MkVucyRodW1hbl9lbnNlbWJsICVpbiUgYWxsSVJFR2VuZXMgJiAhKGh1bWFuX2VudHJlejJFbnMkaHVtYW5fZW5zZW1ibCAlaW4lIGlkcykpLCAKICB1bml2ZXJzZV93aXRob3V0X2lyZSA9IChsZW5ndGgoaHVtYW5fZW50cmV6MkVucyRodW1hbl9lbnNlbWJsKSAtIHVuaXZlcnNlX3dpdGhfaXJlKSwKICAKICBuX3dpdGhfaXJlMyA9IHN1bShpZHMgJWluJSBodW1hbklyZUdlbmVzJGlyZTNfYWxsKSwgICMgTnVtYmVyIG9mIGdlbmVzIGluIHRoZSBnZW5lIHNldCB3aGljaCBoYXZlIDMnIHByZWRpY3RlZCBJUkVzCiAgbl93aXRob3V0X2lyZTM9IChuIC0gbl93aXRoX2lyZSksCiAgdW5pdmVyc2Vfd2l0aF9pcmUzID0gc3VtKGh1bWFuX2VudHJlejJFbnMkaHVtYW5fZW5zZW1ibCAlaW4lIGh1bWFuSXJlR2VuZXMkaXJlM19hbGwgJiAhKGh1bWFuX2VudHJlejJFbnMkaHVtYW5fZW5zZW1ibCAlaW4lIGlkcykpLCAKICB1bml2ZXJzZV93aXRob3V0X2lyZTMgPSAobGVuZ3RoKGh1bWFuX2VudHJlejJFbnMkaHVtYW5fZW5zZW1ibCkgLSB1bml2ZXJzZV93aXRoX2lyZTMpLAogIAogIG5fd2l0aF9pcmU1ID0gc3VtKGlkcyAlaW4lIGh1bWFuSXJlR2VuZXMkaXJlNV9hbGwpLCAgIyBOdW1iZXIgb2YgZ2VuZXMgaW4gdGhlIGdlbmUgc2V0IHdoaWNoIGhhdmUgNScgcHJlZGljdGVkIElSRXMKICBuX3dpdGhvdXRfaXJlNT0gKG4gLSBuX3dpdGhfaXJlKSwKICB1bml2ZXJzZV93aXRoX2lyZTUgPSBzdW0oaHVtYW5fZW50cmV6MkVucyRodW1hbl9lbnNlbWJsICVpbiUgaHVtYW5JcmVHZW5lcyRpcmU1X2FsbCAmICEoaHVtYW5fZW50cmV6MkVucyRodW1hbl9lbnNlbWJsICVpbiUgaWRzKSksIAogIHVuaXZlcnNlX3dpdGhvdXRfaXJlNSA9IChsZW5ndGgoaHVtYW5fZW50cmV6MkVucyRodW1hbl9lbnNlbWJsKSAtIHVuaXZlcnNlX3dpdGhfaXJlNSkKKSAlPiUgdW5ncm91cCgpCgpncwpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpnc19tYXQgPC0gZ3MgJT4lIAogIGRwbHlyOjpzZWxlY3Qobl93aXRoX2lyZSwKICAgICAgICAgICAgICAgIG5fd2l0aG91dF9pcmUsIAogICAgICAgICAgICAgICAgdW5pdmVyc2Vfd2l0aF9pcmUsIAogICAgICAgICAgICAgICAgdW5pdmVyc2Vfd2l0aG91dF9pcmUpICU+JSAKICBhcHBseShYID0gLiwgTUFSR0lOID0gMSwgRlVOID0gZnVuY3Rpb24oeCl7CiAgICB4ICU+JSAKICAgICAgbWF0cml4KDIsMikgJT4lIAogICAgICB0ICU+JSAKICAgICAgbGlzdCgpCiAgfSkgJT4lIHNldF9uYW1lcyhncyRnZW5lc2V0KSAlPiUgCiAgbGFwcGx5KGZ1bmN0aW9uKHgpewogICAgeCAlPiUgLltbMV1dCiAgfSkKCmdzX21hdDMgPC0gZ3MgJT4lIAogIGRwbHlyOjpzZWxlY3Qobl93aXRoX2lyZTMsCiAgICAgICAgICAgICAgICBuX3dpdGhvdXRfaXJlMywgCiAgICAgICAgICAgICAgICB1bml2ZXJzZV93aXRoX2lyZTMsIAogICAgICAgICAgICAgICAgdW5pdmVyc2Vfd2l0aG91dF9pcmUzKSAlPiUgCiAgYXBwbHkoWCA9IC4sIE1BUkdJTiA9IDEsIEZVTiA9IGZ1bmN0aW9uKHgpewogICAgeCAlPiUgCiAgICAgIG1hdHJpeCgyLDIpICU+JSAKICAgICAgdCAlPiUgCiAgICAgIGxpc3QoKQogIH0pICU+JSBzZXRfbmFtZXMoZ3MkZ2VuZXNldCkgJT4lIAogIGxhcHBseShmdW5jdGlvbih4KXsKICAgIHggJT4lIC5bWzFdXQogIH0pCgpnc19tYXQ1IDwtIGdzICU+JSAKICBkcGx5cjo6c2VsZWN0KG5fd2l0aF9pcmU1LAogICAgICAgICAgICAgICAgbl93aXRob3V0X2lyZTUsIAogICAgICAgICAgICAgICAgdW5pdmVyc2Vfd2l0aF9pcmU1LCAKICAgICAgICAgICAgICAgIHVuaXZlcnNlX3dpdGhvdXRfaXJlNSkgJT4lIAogIGFwcGx5KFggPSAuLCBNQVJHSU4gPSAxLCBGVU4gPSBmdW5jdGlvbih4KXsKICAgIHggJT4lIAogICAgICBtYXRyaXgoMiwyKSAlPiUgCiAgICAgIHQgJT4lIAogICAgICBsaXN0KCkKICB9KSAlPiUgc2V0X25hbWVzKGdzJGdlbmVzZXQpICU+JSAKICBsYXBwbHkoZnVuY3Rpb24oeCl7CiAgICB4ICU+JSAuW1sxXV0KICB9KQoKYGBgCmBgYHtyfQpnc19tYXRbMTozXQpnc19tYXQzWzE6M10KZ3NfbWF0NVsxOjNdCmBgYApgYGB7cn0KZmlzaGVyX3JlcyA8LSBnc19tYXQgJT4lIGxhcHBseShmdW5jdGlvbih4KXsKICB4ICU+JSBmaXNoZXIudGVzdCgpCn0pCgpmaXNoZXJfcmVzMyA8LSBnc19tYXQzICU+JSBsYXBwbHkoZnVuY3Rpb24oeCl7CiAgeCAlPiUgZmlzaGVyLnRlc3QoKQp9KQoKZmlzaGVyX3JlczUgPC0gZ3NfbWF0NSAlPiUgbGFwcGx5KGZ1bmN0aW9uKHgpewogIHggJT4lIGZpc2hlci50ZXN0KCkKfSkKCmZpc2hlcl9yZXNfcCA8LSBmaXNoZXJfcmVzICU+JSBsYXBwbHkoZnVuY3Rpb24oeCl7eCRwLnZhbHVlfSkKZmlzaGVyX3Jlc19wMyA8LSBmaXNoZXJfcmVzMyAlPiUgbGFwcGx5KGZ1bmN0aW9uKHgpe3gkcC52YWx1ZX0pCmZpc2hlcl9yZXNfcDUgPC0gZmlzaGVyX3JlczUgJT4lIGxhcHBseShmdW5jdGlvbih4KXt4JHAudmFsdWV9KQoKZ3MgJTw+JSAKICBtdXRhdGUoZmlzaGVyX3AgPSBmaXNoZXJfcmVzX3AlPiV1bmxpc3QlPiV1bm5hbWUsCiAgICAgICAgIGZpc2hlcl9wXzMgPSBmaXNoZXJfcmVzX3AzJT4ldW5saXN0JT4ldW5uYW1lLAogICAgICAgICBmaXNoZXJfcF81ID0gZmlzaGVyX3Jlc19wNSU+JXVubGlzdCU+JXVubmFtZSkgJT4lCiAgbXV0YXRlKGZkciA9IHAuYWRqdXN0KGZpc2hlcl9wLCAiZmRyIiksCiAgICAgICAgIGZkcl8zID0gcC5hZGp1c3QoZmlzaGVyX3BfMywgImZkciIpLAogICAgICAgICBmZHJfNSA9IHAuYWRqdXN0KGZpc2hlcl9wXzUsICJmZHIiKSkKYGBgCgojIyMgQ2FsY3VsYXRlIEV4cGVjdGVkIGFuZCBPYnNlcnZlZAoKRm9yIGVhY2ggZ2VuZSBzZXQsIHdlIHdpbGwgY2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgZ2VuZXMgKipleHBlY3RlZCoqIHRvIGhhdmUgSVJFcyAoYmFzZWQgb24gdGhlIGJhY2tncm91bmQgcHJvcG9ydGlvbikgYW5kICoqb2JzZXJ2ZWQqKiB2YWx1ZS4gClRoaXMgaW5mb3JtYXRpb24gd2lsbCBiZSBhZGRlZCB0byB0aGUgYGdzYCBvYmplY3QuCgpgYGB7ciBldmFsPUZBTFNFfQpncyAlPD4lIHJvd3dpc2UoKSAlPiUgbXV0YXRlKAogIGV4cF9hbGxJUkUgPSAodW5pdmVyc2Vfd2l0aF9pcmUgLyB1bml2ZXJzZV93aXRob3V0X2lyZSkqbl93aXRob3V0X2lyZSwKICBvYnNfYWxsSVJFID0gbl93aXRoX2lyZSwKICBvYnNfZ3JlYXRlcl90aGFuX2V4cF9hbGxJUkUgPSBvYnNfYWxsSVJFID4gZXhwX2FsbElSRSwKICAKICBleHBfaXJlMyA9ICh1bml2ZXJzZV93aXRoX2lyZTMgLyB1bml2ZXJzZV93aXRob3V0X2lyZTMpKm5fd2l0aG91dF9pcmUzLAogIG9ic19pcmUzID0gbl93aXRoX2lyZTMsCiAgb2JzX2dyZWF0ZXJfdGhhbl9leHBfaXJlMyA9IG9ic19pcmUzID4gZXhwX2lyZTMsCiAgCiAgZXhwX2lyZTUgPSAodW5pdmVyc2Vfd2l0aF9pcmU1IC8gdW5pdmVyc2Vfd2l0aG91dF9pcmU1KSpuX3dpdGhvdXRfaXJlNSwKICBvYnNfaXJlNSA9IG5fd2l0aF9pcmU1LAogIG9ic19ncmVhdGVyX3RoYW5fZXhwX2lyZTUgPSBvYnNfaXJlNSA+IGV4cF9pcmU1CikgCmBgYAoKYGBge3J9CmdzICU+JQogIGRwbHlyOjpzZWxlY3QoZ2VuZXNldCwgY29udGFpbnMoImV4cCIpLCBzdGFydHNfd2l0aCgib2JzIiksICkgJT4lIHVuZ3JvdXAoKQpgYGAKCiMjIDYuIFJlc3VsdHMKClRoZSBmb2xsb3dpbmcgdGFibGUgc2hvd3MgdGhlIHRvcCA1MCBnZW5lIHNldHMgZW5yaWNoZWQgaW4gSVJFIGdlbmVzZXRzLCByYW5rZWQgYnkgRmlzaGVyJ3MgZXhhY3QgdGVzdCAqcCotdmFsdWVzOgoKYGBge3J9CmdzICU+JSB1bmdyb3VwKCkgJT4lCiAgYXJyYW5nZShmaXNoZXJfcCkgJT4lCiAgZHBseXI6OnNlbGVjdChnZW5lc2V0LCBjb250YWlucygiZmRyIiksIG5fd2l0aF9pcmUsIG4sIHN0YXJ0c193aXRoKCJvYnNfZ3JlYXRlciIpKSAKCiNncyAlPiUgc2F2ZVJEUyhoZXJlOjpoZXJlKCJSL0dTRUEvZGF0YS9vcmEvaHVtYW5fZ3MucmRzIikpCmdzIDwtIHJlYWRSRFMoaGVyZTo6aGVyZSgiUi9HU0VBL2RhdGEvb3JhL2h1bWFuX2dzLnJkcyIpKQpgYGAKClNvcnRlZCBieSBnZW5lc2V0cyBtb3N0IGVucmljaGVkIGluIDMnIElSRSBnZW5lczoKYGBge3J9CmdzICU+JSB1bmdyb3VwICU+JSBhcnJhbmdlKGZpc2hlcl9wXzMpICU+JSBkcGx5cjo6c2VsZWN0KGdlbmVzZXQsIGNvbnRhaW5zKCJmZHIiKSwgbl93aXRoX2lyZTMsIG4sIHN0YXJ0c193aXRoKCJvYnNfZ3JlYXRlciIpKSAlPiUgaGVhZCg1MCkKYGBgCgpTb3J0ZWQgYnkgZ2VuZXNldHMgbW9zdCBlbnJpY2hlZCBpbiA1JyBJUkUgZ2VuZXM6CmBgYHtyfQpncyAlPiUgdW5ncm91cCAlPiUgYXJyYW5nZShmaXNoZXJfcF81KSAlPiUgZHBseXI6OnNlbGVjdChnZW5lc2V0LCBjb250YWlucygiZmRyIiksIG5fd2l0aF9pcmU1LCBuLCBzdGFydHNfd2l0aCgib2JzX2dyZWF0ZXIiKSkgJT4lIGhlYWQoNTApCmBgYAoKIyMgNy4gVmlzdWFsaXNhdGlvbiAoU3RhY2tlZCBiYXIgY2hhcnQpCgpUaGUgZm9sbG93aW5nIHN0YWNrZWQgYmFyIGNoYXJ0IHNob3dzIHRoZSBvdmVybGFwIGJldHdlZW4gdGhlIHRvcCB+MjAgZ2VuZXNldHMgYW5kIHRoZSBwcmVkaWN0ZWQtSVJFIGdlbmVzZXRzLgoKYGBge3IgfQpvdmVybGFwRGYgPC0gZ3MgJT4lIGFycmFuZ2UoZmlzaGVyX3ApICU+JSBkcGx5cjo6ZmlsdGVyKGZkciA8IDAuMDEgfCBmZHJfMyA8IDAuMDEgfCBmZHJfNSA8IDAuMDEpICU+JQogIHNsaWNlKDE6MTUpICU+JQogIGRwbHlyOjpmaWx0ZXIob2JzX2dyZWF0ZXJfdGhhbl9leHBfYWxsSVJFID09IFRSVUUgfCBvYnNfZ3JlYXRlcl90aGFuX2V4cF9pcmUzID09IFRSVUUgfCBvYnNfZ3JlYXRlcl90aGFuX2V4cF9pcmU1ID09IFRSVUUpICU+JQogIGRwbHlyOjpzZWxlY3QoZ2VuZXNldCwgc291cmNlLCBuX3dpdGhfaXJlMywgbl93aXRoX2lyZTUsIG5fd2l0aG91dF9pcmUpICU+JQogIGJpbmRfcm93cyhkYXRhLmZyYW1lKAogICAgZ2VuZXNldCA9IGMoIi0gQWxsIFByZWRpY3RlZCBJUkVzIiksCiAgICBzb3VyY2UgPSBjKCJzaXJlcyIpLAogICAgbl93aXRoX2lyZTMgPSBjKGxlbmd0aChodW1hbklyZUdlbmVzJGlyZTNfYWxsKSksCiMgICAgICAgICAgICAgICAgICAgIGlyZVV0cjMgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIGRwbHlyOjpmaWx0ZXIocXVhbGl0eSA9PSAiSGlnaCIpICU+JSB1c2Vfc2VyaWVzKCJnZW5lX2lkIikgJT4lIHVuaXF1ZSAlPiUgbGVuZ3RoKSwKICAgIG5fd2l0aF9pcmU1ID0gYyhsZW5ndGgoaHVtYW5JcmVHZW5lcyRpcmU1X2FsbCkpLAojICAgICAgICAgICAgICAgICAgICBpcmVVdHI1ICU+JSBhcy5kYXRhLmZyYW1lICU+JSBkcGx5cjo6ZmlsdGVyKHF1YWxpdHkgPT0gIkhpZ2giKSAlPiUgdXNlX3NlcmllcygiZ2VuZV9pZCIpICU+JSB1bmlxdWUgJT4lIGxlbmd0aCksCiAgICBuX3dpdGhvdXRfaXJlID0gYygwKQogICkpICU+JSBkcGx5cjo6bXV0YXRlKGdlbmVzZXQgPSBwYXN0ZTAoZ2VuZXNldCwgIiAiLCBzb3VyY2UpKSAlPiUKICBkcGx5cjo6bXV0YXRlKGdlbmVzZXQgPSBnc3ViKHg9Z2VuZXNldCwgcGF0dGVybiA9ICJfIiwgcmVwbGFjZW1lbnQgPSAiICIpKSAlPiUKICBkcGx5cjo6c2VsZWN0KC1zb3VyY2UpICU+JSBtZWx0Cgp0b3RhbHMgPC0gb3ZlcmxhcERmICU+JSAKICBkcGx5cjo6YXJyYW5nZShnZW5lc2V0KSAlPiUgCiAgZ3JvdXBfYnkoZ2VuZXNldCkgJT4lIAogIHN1bW1hcmlzZShzdW0gPSBzdW0odmFsdWUpKSAlPiUgCiAgYXJyYW5nZShkZXNjKHN1bSkpCgoKb3ZlcmxhcFBsb3QgPC0gb3ZlcmxhcERmICU+JSAKICBsZWZ0X2pvaW4odG90YWxzKSAlPiUKICBhcnJhbmdlKGRlc2Moc3VtKSkgJT4lCiAgbXV0YXRlKGdlbmVzZXQgPSBmYWN0b3IoZ2VuZXNldCwgbGV2ZWxzID0gdW5pcXVlKGFzLmNoYXJhY3RlcihnZW5lc2V0KSkpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBzdHJfdG9fdGl0bGUoc3RyaW5ncjo6c3RyX3dyYXAoZ2VuZXNldCwgMjgpKSAlPiUgZmFjdG9yKC4sIGxldmVscyA9IHVuaXF1ZShhcy5jaGFyYWN0ZXIoLikpKSwgCiAgICAgICAgICAgICAgICAgICAgIHkgPSB2YWx1ZSwgCiAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSB2YXJpYWJsZSkpICsgCiAgZ2VvbV9jb2wod2lkdGggPSAwLjQpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxLjIsIAogICAgICAgIGF4aXMudGV4dC54ID0gIGVsZW1lbnRfdGV4dChzaXplPSAzNSksIAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPSAzMCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMCksIAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCksIAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTMwKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJncmV5MjAiLCBzaXplID0gMTMpKSArIAogIGNvb3JkX2ZsaXAoKSArICAjIEF4aXMgbGFiZWxzIHRvbyBsb25nIHRvIGJlIHJlYWRhYmxlIHNvIHRoaXMgaGVscHMuCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiNlMTE3NTciLCAiIzM3NjdiMyIsICIjZGRkZGRkIiksIGxhYmVscyA9YygiMycgSVJFIiwgIjUnIElSRSIsICJObyBJUkUiKSkgKwogIGxhYnMoeSA9ICJOdW1iZXIgb2YgZ2VuZXMiLCB4ID0gIkdlbmUgc2V0IiwgZmlsbCA9ICJHZW5lXG5oYXNcbklSRT8iKSArCiAgdGhlbWVfYncoKQoKb3ZlcmxhcFBsb3QKCmV4cG9ydDo6Z3JhcGgycHB0KG92ZXJsYXBQbG90LCBoZXJlKCJSIiwiR1NFQSIsImZpZyIsIm92ZXJsYXBQbG90X2h1bWFuX3RvcDE1IikpCmBgYAoKIyMgOC4gVmlzdWFsaXNhdGlvbiAobmV0d29yayBvdmVybGFwKQoKLSBUaGUgc3RhY2tlZCBiYXIgY2hhcnQgZG9lc24ndCBpbmRpY2F0ZSB0byB1cyB3aGV0aGVyIHRoZSBJUkUgZ2VuZXMgaW4gCmVhY2ggZ2VuZSBzZXQgYXJlIGFjdHVhbGx5IHNoYXJlZCBvciBpZiB0aGV5J3JlIHVuaXF1ZS4gT3ZlcmxhcHBpbmcgc2V0cyAKdGhyb3VnaCBWZW5uIGRpYWdyYW1zIG9yIFVwU2V0IHBsb3RzIHdvdWxkIGJlIGFwcHJvcHJpYXRlIGZvciBzaG93aW5nIHRoaXMsIGJ1dCAKSSdtIGdvaW5nIHRvIHVzZSBhIG5ldHdvcmsgcGxvdCBmb3IgdGhlIGZvbGxvd2luZyByZWFzb25zOgoKICAgIC0gVGhlcmUgYXJlIH4xNSBzZXRzIHdoaWNoIGFyZSB0byBiZSBwbG90dGVkLiBBbHRob3VnaCBVcFNldCBjb3BlcyBPSyB3aXRoIAogICAgdGhpcywgdGhlIG1haW4gcG9pbnQgd2hpY2ggSSB3YW50IHRvIGVtcGhhc2lzZSBpcyBob3cgbWFueSBJUkUgZ2VuZXMgCiAgICBhcmUgVU5JUVVFIHRvIHRoZSBwcmVkaWN0ZWQgSVJFIGdlbmUgc2V0cz8gCiAgICAtIE5ldHdvcmsgbGF5b3V0IGFsZ29yaXRobXMgY2FuIGJlIGluZm9ybWF0aXZlIGluIHNob3dpbmcgdXMgZXhhY3RseSB0aGlzIAogICAgYWxvbmcgd2l0aCBnaXZpbmcgYW4gYXBwcmVjaWF0aW9uIG9mIHRoZSBzY2FsZSAoZS5nLiBob3cgbWFueSBnZW5lcyAKICAgIGFyZSBpbnZvbHZlZCByZWxhdGl2ZWx5IGluIGVhY2ggZ2VuZSBzZXQgLyBhcmUgc2hhcmVkKS4gCgotIFRvIHVzZSBHZXBoaSB0byBwcm9kdWNlIGEgbmV0d29yayBwbG90LCB3ZSBuZWVkIGEgKipub2RlcyoqIHRhYmxlIGFuZCAKYW4gKiplZGdlcyoqIHRhYmxlLiBIZXJlIEkgd2lsbCBwcm9kdWNlIHRoZSBub2RlcyB0YWJsZSB1c2luZyB0aGUgdG9wIAoxNSBnZW5lIHNldHMgcmFua2VkIGJ5ICpwKi12YWx1ZS4gCgotIFdlIHdpbGwgYWxzbyBhcHBlbmQgdGhlIHByZWRpY3RlZCBJUkUgZ2VuZSBzZXRzIGFuZCB0aGUgIkhhbGxtYXJrIApIZW1lIE1ldGFib2xpc20iIGdlbmUgc2V0IGFzIHRoaXMgb25lIGlzIGNvbnNpZGVyZWQgbGlrZSBhICJnb2xkIHN0YW5kYXJkIiBhbmQgCnRoZSBmaXJzdCBwb2ludCBmb3IgdGVzdGluZyBmb3IgbWFueSBhcyBpdCdzIGluY2x1ZGVkIGluIHRoZSBIYWxsbWFyayAKY29sbGVjdGlvbi4gCgpgYGB7cn0KIyBJbmNsdWRlIHRoZSBmb2xsb3dpbmcgdG9wIHJhbmtlZCBnZW5lIHNldHMgKH4xNSkgaW4gdGhlIG5ldHdvcmsgCm5vZGVzIDwtIGdzICU+JSBhcnJhbmdlKGZpc2hlcl9wKSAlPiUgCiAgZHBseXI6OnNsaWNlKDE6MTUpICU+JQogIGRwbHlyOjpmaWx0ZXIob2JzX2dyZWF0ZXJfdGhhbl9leHBfYWxsSVJFID09IFRSVUUgfCAKICAgICAgICAgICAgICAgICAgb2JzX2dyZWF0ZXJfdGhhbl9leHBfaXJlMyA9PSBUUlVFIHwgCiAgICAgICAgICAgICAgICAgIG9ic19ncmVhdGVyX3RoYW5fZXhwX2lyZTUgPT0gVFJVRSkgJT4lCiAgdW5ncm91cAoKIyBBcHBlbmQgaW5mb3JtYXRpb24gYWJvdXQgMycgYW5kIDUnIHByZWRpY3RlZCBJUkUgZ2VuZXNldHMgCm5vZGVzICU8PiUgYmluZF9yb3dzKAogICB0aWJibGUoCiAgICAgaWRzID0gYyhsaXN0KGh1bWFuSXJlR2VuZXMkaXJlM19hbGwsIGh1bWFuSXJlR2VuZXMkaXJlNV9hbGwpKSwKICAgICBnZW5lc2V0ID0gYygiUHJlZGljdGVkIDMnIElSRSBnZW5lcyIsICJQcmVkaWN0ZWQgNScgSVJFIGdlbmVzIiksCiAgICAgc291cmNlID0gYygic2lyZXMiLCJzaXJlcyIpLAogICAgIG4gPSBjKGxlbmd0aChodW1hbklyZUdlbmVzJGlyZTNfYWxsKSwgbGVuZ3RoKGh1bWFuSXJlR2VuZXMkaXJlNV9hbGwpKQogICApCiApIAoKIyBBcHBlbmQgdGhlIEhhbGxtYXJrIEhlbWUgTWV0YWJvbGlzbSBnZW5lc2V0CiMgQWx0aG91Z2ggdGhpcyBnZW5lIHNldCB3YXNudCBzaWduaWZpY2FudGx5IGVucmljaGVkIGluIAojIElSRS1jb250YWluaW5nIGdlbmVzLCBpdCBpcyBwcm9iYWJseSB0aGUgbW9zdCBjb21wcmVoZW5zaXZlCiMgZ2VuZSBzZXQgc3BlY2lmaWNhbGx5IG9uIGhlbWUgbWV0YWJvbGlzbSwgYW5kIGNvbWJpbmVzIGluZm8gCiMgZnJvbSB2YXJpb3VzIHN0dWRpZXMuIApub2RlcyAlPD4lIGJpbmRfcm93cygKICBoX3RpYiAlPiUgZmlsdGVyKGdlbmVzZXQgPT0gIkhBTExNQVJLX0hFTUVfTUVUQUJPTElTTSIpICU+JSAKICAgIG11dGF0ZShuID0gbGVuZ3RoKGlkc1tbMV1dKSkKICkgCgpub2RlcwpgYGAKCi0gQmVsb3csIHdlIGNyZWF0ZSBhIGRhdGEuZnJhbWUgdG8gaG9sZCBpbmZvcm1hdGlvbiBhYm91dCBnZW5lIGludGVyc2VjdGlvbnMgCmZyb20gZWFjaCBnZW5lIHNldC4gCgpgYGB7cn0KZ3NDb21iIDwtIGNvbWJuKG5vZGVzJGdlbmVzZXQsIG0gPSAyKSAlPiUgCiAgdCAlPiUgCiAgYXMuZGF0YS5mcmFtZSAlPiUKICBzZXRfY29sbmFtZXMoYygiZ2VuZXNldCIsICJnZW5lc2V0XzJiIikpICU+JQogIGxlZnRfam9pbihub2RlcyU+JWRwbHlyOjpzZWxlY3QoaWRzLCBnZW5lc2V0KSwgYnkgPSAiZ2VuZXNldCIpICU+JQogIGRwbHlyOjpyZW5hbWUoZ2VuZXNldF9ubSA9IGdlbmVzZXQsCiAgICAgICAgICAgICAgICBnZW5lc2V0ID0gZ2VuZXNldF8yYikgJT4lCiAgbGVmdF9qb2luKG5vZGVzJT4lZHBseXI6OnNlbGVjdChpZHMsIGdlbmVzZXQpLCBieSA9ICJnZW5lc2V0IikgJT4lCiAgYXNfdGliYmxlICU+JQogIG11dGF0ZShjb21tb25faWRzID0gbWFwMihpZHMueCwgaWRzLnksIH5pbnRlcnNlY3QoLngsLnkpKSkKCmdzQ29tYiAKYGBgCgotIFdlIGNhbiBleHRyYWN0IGdlbmVzIHdoaWNoIGFwcGVhciB0aGUgbW9zdCBvZnRlbiBpbiBnZW5lc2V0cyBhcyBmb2xsb3dzOgoKYGBge3J9CmdzQ29tYiRjb21tb25faWRzJT4lCiAgdW5saXN0ICU+JSAKICB0YWJsZSAlPiUgCiAgYXMuZGF0YS5mcmFtZSU+JQogIGFycmFuZ2UoZGVzYyhGcmVxKSkgJT4lCiAgc2V0X2NvbG5hbWVzKGMoImVuc2VtYmxfZ2VuZV9pZCIsICJmcmVxIikpICAlPiUgaGVhZApgYGAKCi0gVGhlIGZpbmFsIG5vZGVzIHRhYmxlIGZvciBnZXBoaSB3aWxsIGNvbnRhaW4KCiAgICAtIEFsbCBnZW5lcyBpbiB0aGUgZ2VuZXNldHMKICAgIC0gVGhlIGdlbmVzZXQgbmFtZXMKCmBgYHtyfQpwYXRod2F5cyA8LSBub2RlcyRnZW5lc2V0ICU+JSBhcy5kYXRhLmZyYW1lICU+JSBzZXRfY29sbmFtZXMoImxhYmVsIikKZ2VuZXMgPC0gbm9kZXMkaWRzICU+JSB1bmxpc3QgJT4lIHVuaXF1ZSAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgc2V0X2NvbG5hbWVzKCJsYWJlbCIpCm5vZGVzRGYgPC0gZnVsbF9qb2luKHBhdGh3YXlzLCBnZW5lcywgYnkgPSAibGFiZWwiKSAlPiUgcm93aWRfdG9fY29sdW1uKCJpZCIpCgojIG5vZGVzRGYgJT4lCiMgICBtdXRhdGUodGV4dCA9IGlmZWxzZShpZCA8IDE4LCBsYWJlbCwgTkEpKSAlPiUKIyAgIG11dGF0ZShzaXplID0gaWZlbHNlKGlkIDwgMTYsIDIsIDEpKSAlPiUKIyAgIG11dGF0ZShjb2xvdXIgPSBpZmVsc2UoaWQgPCAxNiwgcmFpbmJvdygyNilbaWRdLCBOQSkpIAoKaGVhZChub2Rlc0RmLDIwKQoKbm9kZXNEZjIgPC0gbm9kZXNEZiAlPiUKICBtdXRhdGUoCiAgICBpcmUgPSBjYXNlX3doZW4oCiAgICAgIGlkIDwgMTYgfiAiICIsCiAgICAgIGlkID09IDE4IH4gIiAiLAogICAgICBpZCA9PSAxNiB+ICIzIiwKICAgICAgaWQgPT0gMTcgfiAiNSIsCiAgICAgIGxhYmVsICVpbiUgaHVtYW5JcmVHZW5lcyRpcmUzX2FsbCB+ICIzIiwKICAgICAgbGFiZWwgJWluJSBodW1hbklyZUdlbmVzJGlyZTVfYWxsIH4gIjUiLAogICAgICAhKGxhYmVsICVpbiUgYWxsSVJFR2VuZXMpIH4gIm5vIElSRSIKICAgICkKICApJT4lCiAgZHBseXI6OnNlbGVjdCgtaWQpICU+JSBkcGx5cjo6cmVuYW1lKElkID0gbGFiZWwpIAoKaGVhZChub2Rlc0RmMiwgMzApIAogbm9kZXNEZjIgJT4lIHdyaXRlX3RzdihoZXJlKCJSIiwiR1NFQSIsImRhdGEiLCJodW1hbl9uZXR3b3JrIiwibm9kZXMudHN2IikpCmBgYAoKLSBFZGdlcyB3aWxsIGJlIGJldHdlZW46CgogICAgLSBUaGUgZ2VuZXNldHMgYW5kIGFsbCBnZW5lcyB3aXRoaW4gdGhlIGdlbmVzZXQgCiAgICAtIEdlbmVzIGluIG11bHRpcGxlIGdlbmVzZXRzCgpgYGB7cn0KZWRnZURmIDwtIG5vZGVzJGlkcyAlPiUgc2V0X25hbWVzKG5vZGVzJGdlbmVzZXQpICU+JSBwbHlyOjpsZHBseShkYXRhLmZyYW1lKSAlPiUgCiAgc2V0X2NvbG5hbWVzKGMoInBhdGh3YXkiLCAiZ2VuZSIpKSAlPiUKICBsZWZ0X2pvaW4obm9kZXNEZiwgYnkgPSBjKCJwYXRod2F5Ij0ibGFiZWwiKSkgJT4lCiAgZHBseXI6OnJlbmFtZShmcm9tID0gaWQpICU+JQogIGxlZnRfam9pbihub2Rlc0RmLCBieSA9IGMoImdlbmUiPSJsYWJlbCIpKSAlPiUKICBkcGx5cjo6cmVuYW1lKHRvPWlkKSAlPiUKICAjZHBseXI6OnNlbGVjdChmcm9tLCB0bykKICBkcGx5cjo6c2VsZWN0KHBhdGh3YXksIGdlbmUpICU+JQogIHNldF9jb2xuYW1lcyhjKCJTb3VyY2UiLCJUYXJnZXQiKSkKZWRnZURmICU+JSBoZWFkKDIwKQoKd3JpdGVfdHN2KGVkZ2VEZiwgaGVyZTo6aGVyZSgiUi9HU0VBL2RhdGEvaHVtYW5fbmV0d29yay9lZGdlcy50c3YiKSkKCmBgYAoKIyMgOS4gU2Vzc2lvbiBJbmZvCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCgo=